home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / PIL / PngImagePlugin.py < prev    next >
Text File  |  2006-12-03  |  15KB  |  559 lines

  1. #
  2. # The Python Imaging Library.
  3. # $Id: PngImagePlugin.py 2203 2004-12-19 14:32:32Z fredrik $
  4. #
  5. # PNG support code
  6. #
  7. # See "PNG (Portable Network Graphics) Specification, version 1.0;
  8. # W3C Recommendation", 1996-10-01, Thomas Boutell (ed.).
  9. #
  10. # history:
  11. # 1996-05-06 fl   Created (couldn't resist it)
  12. # 1996-12-14 fl   Upgraded, added read and verify support (0.2)
  13. # 1996-12-15 fl   Separate PNG stream parser
  14. # 1996-12-29 fl   Added write support, added getchunks
  15. # 1996-12-30 fl   Eliminated circular references in decoder (0.3)
  16. # 1998-07-12 fl   Read/write 16-bit images as mode I (0.4)
  17. # 2001-02-08 fl   Added transparency support (from Zircon) (0.5)
  18. # 2001-04-16 fl   Don't close data source in "open" method (0.6)
  19. # 2004-02-24 fl   Don't even pretend to support interlaced files (0.7)
  20. # 2004-08-31 fl   Do basic sanity check on chunk identifiers (0.8)
  21. # 2004-09-20 fl   Added PngInfo chunk container
  22. # 2004-12-18 fl   Added DPI read support (based on code by Niki Spahiev)
  23. #
  24. # Copyright (c) 1997-2004 by Secret Labs AB
  25. # Copyright (c) 1996 by Fredrik Lundh
  26. #
  27. # See the README file for information on usage and redistribution.
  28. #
  29.  
  30. __version__ = "0.8.2"
  31.  
  32. import re, string
  33.  
  34. import Image, ImageFile, ImagePalette
  35.  
  36.  
  37. def i16(c):
  38.     return ord(c[1]) + (ord(c[0])<<8)
  39. def i32(c):
  40.     return ord(c[3]) + (ord(c[2])<<8) + (ord(c[1])<<16) + (ord(c[0])<<24)
  41.  
  42. is_cid = re.compile("\w\w\w\w").match
  43.  
  44.  
  45. _MAGIC = "\211PNG\r\n\032\n"
  46.  
  47.  
  48. _MODES = {
  49.     # supported bits/color combinations, and corresponding modes/rawmodes
  50.     (1, 0): ("1", "1"),
  51.     (2, 0): ("L", "L;2"),
  52.     (4, 0): ("L", "L;4"),
  53.     (8, 0): ("L", "L"),
  54.     (16,0): ("I", "I;16B"),
  55.     (8, 2): ("RGB", "RGB"),
  56.     (16,2): ("RGB", "RGB;16B"),
  57.     (1, 3): ("P", "P;1"),
  58.     (2, 3): ("P", "P;2"),
  59.     (4, 3): ("P", "P;4"),
  60.     (8, 3): ("P", "P"),
  61.     (8, 4): ("LA", "LA"),
  62.     (16,4): ("RGBA", "LA;16B"), # LA;16B->LA not yet available
  63.     (8, 6): ("RGBA", "RGBA"),
  64.     (16,6): ("RGBA", "RGBA;16B"),
  65. }
  66.  
  67.  
  68. # --------------------------------------------------------------------
  69. # Support classes.  Suitable for PNG and related formats like MNG etc.
  70.  
  71. class ChunkStream:
  72.  
  73.     def __init__(self, fp):
  74.  
  75.         self.fp = fp
  76.         self.queue = []
  77.  
  78.         if not hasattr(Image.core, "crc32"):
  79.             self.crc = self.crc_skip
  80.  
  81.     def read(self):
  82.         "Fetch a new chunk. Returns header information."
  83.  
  84.         if self.queue:
  85.             cid, pos, len = self.queue[-1]
  86.             del self.queue[-1]
  87.             self.fp.seek(pos)
  88.         else:
  89.             s = self.fp.read(8)
  90.             cid = s[4:]
  91.             pos = self.fp.tell()
  92.             len = i32(s)
  93.  
  94.         if not is_cid(cid):
  95.             raise SyntaxError, "broken PNG file (chunk %s)" % repr(cid)
  96.  
  97.         return cid, pos, len
  98.  
  99.     def close(self):
  100.         self.queue = self.crc = self.fp = None
  101.  
  102.     def push(self, cid, pos, len):
  103.  
  104.         self.queue.append((cid, pos, len))
  105.  
  106.     def call(self, cid, pos, len):
  107.         "Call the appropriate chunk handler"
  108.  
  109.         if Image.DEBUG:
  110.             print "STREAM", cid, pos, len
  111.         return getattr(self, "chunk_" + cid)(pos, len)
  112.  
  113.     def crc(self, cid, data):
  114.         "Read and verify checksum"
  115.  
  116.         crc1 = Image.core.crc32(data, Image.core.crc32(cid))
  117.         crc2 = i16(self.fp.read(2)), i16(self.fp.read(2))
  118.         if crc1 != crc2:
  119.             raise SyntaxError, "broken PNG file"\
  120.                 "(bad header checksum in %s)" % cid
  121.  
  122.     def crc_skip(self, cid, data):
  123.         "Read checksum.  Used if the C module is not present"
  124.  
  125.         self.fp.read(4)
  126.  
  127.     def verify(self, endchunk = "IEND"):
  128.  
  129.         # Simple approach; just calculate checksum for all remaining
  130.         # blocks.  Must be called directly after open.
  131.  
  132.         cids = []
  133.  
  134.         while 1:
  135.             cid, pos, len = self.read()
  136.             if cid == endchunk:
  137.                 break
  138.             self.crc(cid, ImageFile._safe_read(self.fp, len))
  139.             cids.append(cid)
  140.  
  141.         return cids
  142.  
  143.  
  144. # --------------------------------------------------------------------
  145. # PNG chunk container (for use with save(pnginfo=))
  146.  
  147. class PngInfo:
  148.  
  149.     def __init__(self):
  150.         self.chunks = []
  151.  
  152.     def add(self, cid, data):
  153.         self.chunks.append((cid, data))
  154.  
  155.     def add_text(self, key, value, zip=0):
  156.         if zip:
  157.             import zlib
  158.             self.add("zTXt", key + "\0\0" + zlib.compress(value))
  159.         else:
  160.             self.add("tEXt", key + "\0" + value)
  161.  
  162. # --------------------------------------------------------------------
  163. # PNG image stream (IHDR/IEND)
  164.  
  165. class PngStream(ChunkStream):
  166.  
  167.     def __init__(self, fp):
  168.  
  169.         ChunkStream.__init__(self, fp)
  170.  
  171.         # local copies of Image attributes
  172.         self.im_info = {}
  173.         self.im_size = (0,0)
  174.         self.im_mode = None
  175.         self.im_tile = None
  176.         self.im_palette = None
  177.  
  178.     def chunk_IHDR(self, pos, len):
  179.  
  180.         # image header
  181.         s = ImageFile._safe_read(self.fp, len)
  182.         self.im_size = i32(s), i32(s[4:])
  183.         try:
  184.             self.im_mode, self.im_rawmode = _MODES[(ord(s[8]), ord(s[9]))]
  185.         except:
  186.             pass
  187.         if ord(s[12]):
  188.             self.im_info["interlace"] = 1
  189.         if ord(s[11]):
  190.             raise SyntaxError, "unknown filter category"
  191.         return s
  192.  
  193.     def chunk_IDAT(self, pos, len):
  194.  
  195.         # image data
  196.         self.im_tile = [("zip", (0,0)+self.im_size, pos, self.im_rawmode)]
  197.         self.im_idat = len
  198.         raise EOFError
  199.  
  200.     def chunk_IEND(self, pos, len):
  201.  
  202.         # end of PNG image
  203.         raise EOFError
  204.  
  205.     def chunk_PLTE(self, pos, len):
  206.  
  207.         # palette
  208.         s = ImageFile._safe_read(self.fp, len)
  209.         if self.im_mode == "P":
  210.             self.im_palette = "RGB", s
  211.         return s
  212.  
  213.     def chunk_tRNS(self, pos, len):
  214.  
  215.         # transparency
  216.         s = ImageFile._safe_read(self.fp, len)
  217.         if self.im_mode == "P":
  218.             i = string.find(s, chr(0))
  219.             if i >= 0:
  220.                 self.im_info["transparency"] = i
  221.         elif self.im_mode == "L":
  222.             self.im_info["transparency"] = i16(s)
  223.         return s
  224.  
  225.     def chunk_gAMA(self, pos, len):
  226.  
  227.         # gamma setting
  228.         s = ImageFile._safe_read(self.fp, len)
  229.         self.im_info["gamma"] = i32(s) / 100000.0
  230.         return s
  231.  
  232.     def chunk_pHYs(self, pos, len):
  233.  
  234.         # pixels per unit
  235.         s = ImageFile._safe_read(self.fp, len)
  236.         px, py = i32(s), i32(s[4:])
  237.         unit = ord(s[8])
  238.         if unit == 1: # meter
  239.             dpi = int(px * 0.0254 + 0.5), int(py * 0.0254 + 0.5)
  240.             self.im_info["dpi"] = dpi
  241.         elif unit == 0:
  242.             self.im_info["aspect"] = px, py
  243.         return s
  244.  
  245.     def chunk_tEXt(self, pos, len):
  246.  
  247.         # text
  248.         s = ImageFile._safe_read(self.fp, len)
  249.         try:
  250.             k, v = string.split(s, "\0", 1)
  251.         except ValueError:
  252.             k = s; v = "" # fallback for broken tEXt tags
  253.         if k:
  254.             self.im_info[k] = v
  255.         return s
  256.  
  257.  
  258. # --------------------------------------------------------------------
  259. # PNG reader
  260.  
  261. def _accept(prefix):
  262.     return prefix[:8] == _MAGIC
  263.  
  264. ##
  265. # Image plugin for PNG images.
  266.  
  267. class PngImageFile(ImageFile.ImageFile):
  268.  
  269.     format = "PNG"
  270.     format_description = "Portable network graphics"
  271.  
  272.     def _open(self):
  273.  
  274.         if self.fp.read(8) != _MAGIC:
  275.             raise SyntaxError, "not a PNG file"
  276.  
  277.         #
  278.         # Parse headers up to the first IDAT chunk
  279.  
  280.         self.png = PngStream(self.fp)
  281.  
  282.         while 1:
  283.  
  284.             #
  285.             # get next chunk
  286.  
  287.             cid, pos, len = self.png.read()
  288.  
  289.             try:
  290.                 s = self.png.call(cid, pos, len)
  291.             except EOFError:
  292.                 break
  293.             except AttributeError:
  294.                 if Image.DEBUG:
  295.                     print cid, pos, len, "(unknown)"
  296.                 s = ImageFile._safe_read(self.fp, len)
  297.  
  298.             self.png.crc(cid, s)
  299.  
  300.         #
  301.         # Copy relevant attributes from the PngStream.  An alternative
  302.         # would be to let the PngStream class modify these attributes
  303.         # directly, but that introduces circular references which are
  304.         # difficult to break if things go wrong in the decoder...
  305.         # (believe me, I've tried ;-)
  306.  
  307.         self.mode = self.png.im_mode
  308.         self.size = self.png.im_size
  309.         self.info = self.png.im_info
  310.         self.tile = self.png.im_tile
  311.  
  312.         if self.png.im_palette:
  313.             rawmode, data = self.png.im_palette
  314.             self.palette = ImagePalette.raw(rawmode, data)
  315.  
  316.         self.__idat = len # used by load_read()
  317.  
  318.  
  319.     def verify(self):
  320.         "Verify PNG file"
  321.  
  322.         if self.fp is None:
  323.             raise RuntimeError("verify must be called directly after open")
  324.  
  325.         # back up to beginning of IDAT block
  326.         self.fp.seek(self.tile[0][2] - 8)
  327.  
  328.         self.png.verify()
  329.         self.png.close()
  330.  
  331.         self.fp = None
  332.  
  333.     def load_prepare(self):
  334.         "internal: prepare to read PNG file"
  335.  
  336.         if self.info.get("interlace"):
  337.             raise IOError("cannot read interlaced PNG files")
  338.  
  339.         ImageFile.ImageFile.load_prepare(self)
  340.  
  341.     def load_read(self, bytes):
  342.         "internal: read more image data"
  343.  
  344.         while self.__idat == 0:
  345.             # end of chunk, skip forward to next one
  346.  
  347.             self.fp.read(4) # CRC
  348.  
  349.             cid, pos, len = self.png.read()
  350.  
  351.             if cid not in ["IDAT", "DDAT"]:
  352.                 self.png.push(cid, pos, len)
  353.                 return ""
  354.  
  355.             self.__idat = len # empty chunks are allowed
  356.  
  357.         # read more data from this chunk
  358.         if bytes <= 0:
  359.             bytes = self.__idat
  360.         else:
  361.             bytes = min(bytes, self.__idat)
  362.  
  363.         self.__idat = self.__idat - bytes
  364.  
  365.         return self.fp.read(bytes)
  366.  
  367.  
  368.     def load_end(self):
  369.         "internal: finished reading image data"
  370.  
  371.         self.png.close()
  372.         self.png = None
  373.  
  374.  
  375. # --------------------------------------------------------------------
  376. # PNG writer
  377.  
  378. def o16(i):
  379.     return chr(i>>8&255) + chr(i&255)
  380.  
  381. def o32(i):
  382.     return chr(i>>24&255) + chr(i>>16&255) + chr(i>>8&255) + chr(i&255)
  383.  
  384. _OUTMODES = {
  385.     # supported PIL modes, and corresponding rawmodes/bits/color combinations
  386.     "1":   ("1", chr(1)+chr(0)),
  387.     "L;1": ("L;1", chr(1)+chr(0)),
  388.     "L;2": ("L;2", chr(2)+chr(0)),
  389.     "L;4": ("L;4", chr(4)+chr(0)),
  390.     "L":   ("L", chr(8)+chr(0)),
  391.     "LA":  ("LA", chr(8)+chr(4)),
  392.     "I":   ("I;16B", chr(16)+chr(0)),
  393.     "P;1": ("P;1", chr(1)+chr(3)),
  394.     "P;2": ("P;2", chr(2)+chr(3)),
  395.     "P;4": ("P;4", chr(4)+chr(3)),
  396.     "P":   ("P", chr(8)+chr(3)),
  397.     "RGB": ("RGB", chr(8)+chr(2)),
  398.     "RGBA":("RGBA", chr(8)+chr(6)),
  399. }
  400.  
  401. def putchunk(fp, cid, *data):
  402.     "Write a PNG chunk (including CRC field)"
  403.  
  404.     data = string.join(data, "")
  405.  
  406.     fp.write(o32(len(data)) + cid)
  407.     fp.write(data)
  408.     hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
  409.     fp.write(o16(hi) + o16(lo))
  410.  
  411. class _idat:
  412.     # wrap output from the encoder in IDAT chunks
  413.  
  414.     def __init__(self, fp, chunk):
  415.         self.fp = fp
  416.         self.chunk = chunk
  417.     def write(self, data):
  418.         self.chunk(self.fp, "IDAT", data)
  419.  
  420. def _save(im, fp, filename, chunk=putchunk, check=0):
  421.     # save an image to disk (called by the save method)
  422.  
  423.     mode = im.mode
  424.  
  425.     if mode == "P":
  426.  
  427.         #
  428.         # attempt to minimize storage requirements for palette images
  429.  
  430.         if im.encoderinfo.has_key("bits"):
  431.  
  432.             # number of bits specified by user
  433.             n = 1 << im.encoderinfo["bits"]
  434.  
  435.         else:
  436.  
  437.             # check palette contents
  438.             n = 256 # FIXME
  439.  
  440.         if n <= 2:
  441.             bits = 1
  442.         elif n <= 4:
  443.             bits = 2
  444.         elif n <= 16:
  445.             bits = 4
  446.         else:
  447.             bits = 8
  448.  
  449.         if bits != 8:
  450.             mode = "%s;%d" % (mode, bits)
  451.  
  452.     # encoder options
  453.     if im.encoderinfo.has_key("dictionary"):
  454.         dictionary = im.encoderinfo["dictionary"]
  455.     else:
  456.         dictionary = ""
  457.  
  458.     im.encoderconfig = (im.encoderinfo.has_key("optimize"), dictionary)
  459.  
  460.     # get the corresponding PNG mode
  461.     try:
  462.         rawmode, mode = _OUTMODES[mode]
  463.     except KeyError:
  464.         raise IOError, "cannot write mode %s as PNG" % mode
  465.  
  466.     if check:
  467.         return check
  468.  
  469.     #
  470.     # write minimal PNG file
  471.  
  472.     fp.write(_MAGIC)
  473.  
  474.     chunk(fp, "IHDR",
  475.           o32(im.size[0]), o32(im.size[1]),     #  0: size
  476.           mode,                                 #  8: depth/type
  477.           chr(0),                               # 10: compression
  478.           chr(0),                               # 11: filter category
  479.           chr(0))                               # 12: interlace flag
  480.  
  481.     if im.mode == "P":
  482.         chunk(fp, "PLTE", im.im.getpalette("RGB"))
  483.  
  484.     if im.encoderinfo.has_key("transparency"):
  485.         if im.mode == "P":
  486.             transparency = max(0, min(255, im.encoderinfo["transparency"]))
  487.             chunk(fp, "tRNS", chr(255) * transparency + chr(0))
  488.         elif im.mode == "L":
  489.             transparency = max(0, min(65535, im.encoderinfo["transparency"]))
  490.             chunk(fp, "tRNS", o16(transparency))
  491.         else:
  492.             raise IOError, "cannot use transparency for this mode"
  493.  
  494.     if 0:
  495.         # FIXME: to be supported some day
  496.         chunk(fp, "gAMA", o32(int(gamma * 100000.0)))
  497.  
  498.     dpi = im.encoderinfo.get("dpi")
  499.     if dpi:
  500.         chunk(fp, "pHYs",
  501.               o32(int(dpi[0] / 0.0254 + 0.5)),
  502.               o32(int(dpi[1] / 0.0254 + 0.5)),
  503.               chr(1))
  504.  
  505.     info = im.encoderinfo.get("pnginfo")
  506.     if info:
  507.         for cid, data in info.chunks:
  508.             chunk(fp, cid, data)
  509.  
  510.     ImageFile._save(im, _idat(fp, chunk), [("zip", (0,0)+im.size, 0, rawmode)])
  511.  
  512.     chunk(fp, "IEND", "")
  513.  
  514.     try:
  515.         fp.flush()
  516.     except:
  517.         pass
  518.  
  519.  
  520. # --------------------------------------------------------------------
  521. # PNG chunk converter
  522.  
  523. def getchunks(im, **params):
  524.     """Return a list of PNG chunks representing this image."""
  525.  
  526.     class collector:
  527.         data = []
  528.         def write(self, data):
  529.             pass
  530.         def append(self, chunk):
  531.             self.data.append(chunk)
  532.  
  533.     def append(fp, cid, *data):
  534.         data = string.join(data, "")
  535.         hi, lo = Image.core.crc32(data, Image.core.crc32(cid))
  536.         crc = o16(hi) + o16(lo)
  537.         fp.append((cid, data, crc))
  538.  
  539.     fp = collector()
  540.  
  541.     try:
  542.         im.encoderinfo = params
  543.         _save(im, fp, None, append)
  544.     finally:
  545.         del im.encoderinfo
  546.  
  547.     return fp.data
  548.  
  549.  
  550. # --------------------------------------------------------------------
  551. # Registry
  552.  
  553. Image.register_open("PNG", PngImageFile, _accept)
  554. Image.register_save("PNG", _save)
  555.  
  556. Image.register_extension("PNG", ".png")
  557.  
  558. Image.register_mime("PNG", "image/png")
  559.